package com.learning.algorithm.util;

import com.hankcs.hanlp.HanLP;
import lombok.extern.log4j.Log4j2;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author: daqian_liao
 * @Date: 2018/12/12 13:55
 * @Version 1.0
 */
@Log4j2
public class SimilarityUtil {

    /**
     * 获得两个句子的余弦相似度
     *
     * @param sentence1
     * @param sentence2
     * @return
     */
    public static double getSimilarity(String sentence1, String sentence2) {
        List<String> sent1Words = SegmentationUtil.getSplitWords(sentence1);
        List<String> sent2Words = SegmentationUtil.getSplitWords(sentence2);
        List<String> allWords = mergeList(sent1Words, sent2Words);

        int[] statistic1 = statistic(allWords, sent1Words);
        int[] statistic2 = statistic(allWords, sent2Words);

        double dividend = 0;
        double divisor1 = 0;
        double divisor2 = 0;
        for (int i = 0; i < statistic1.length; i++) {
            dividend += statistic1[i] * statistic2[i];
            divisor1 += Math.pow(statistic1[i], 2);
            divisor2 += Math.pow(statistic2[i], 2);
        }

        return 100 *  dividend / (Math.sqrt(divisor1) * Math.sqrt(divisor2));
    }

    private static int[] statistic(List<String> allWords, List<String> sentWords) {
        int[] result = new int[allWords.size()];
        for (int i = 0; i < allWords.size(); i++) {
            result[i] = Collections.frequency(sentWords, allWords.get(i));
        }
        return result;
    }

    private static List<String> mergeList(List<String> list1, List<String> list2) {
        List<String> result = new ArrayList<>();
        result.addAll(list1);
        result.addAll(list2);
        return result.stream().distinct().collect(Collectors.toList());
    }

    public static double levenshtein(String str1, String str2) {
        int len1 = str1.length();
        int len2 = str2.length();
        int[][] dif = new int[len1 + 1][len2 + 1];
        for (int a = 0; a <= len1; a++) {
            dif[a][0] = a;
        }
        for (int a = 0; a <= len2; a++) {
            dif[0][a] = a;
        }

        int temp;
        for (int i = 1; i <= len1; i++) {
            for (int j = 1; j <= len2; j++) {
                temp = str1.charAt(i - 1) == str2.charAt(j - 1) ? 0 : 1;
                int result = min(dif[i - 1][j - 1] + temp, dif[i][j - 1] + 1,
                        dif[i - 1][j] + 1);
                dif[i][j] = result;
            }
//            log.info("dif({}) = {}", i, dif[i]);
        }
        log.info("字符串\"" + str1 + "\"与\"" + str2 + "\"的比较");
        log.info("差异步骤：" + dif[len1][len2]);
        double similarity = 1 - (double) dif[len1][len2] / Math.max(str1.length(), str2.length());
        log.info("相似度：" + similarity);
        return 100 * similarity;
    }

    //得到最小值
    private static int min(int... is) {
        int min = Integer.MAX_VALUE;
        for (int i : is) {
            if (min > i) {
                min = i;
            }
        }
        return min;
    }


    public static void main(String[] args) {
        log.info(getSimilarity("cai,jia,ming,shabi", "cai,jia,ming,shabi"));
        log.info(getSimilarity("cai,jia,ming,shabi", "cai,jia,min,"));

        log.info(HanLP.segment("nihao, caijiaming "));

        levenshtein("蒹葭深圳", "蒹葭");
        levenshtein("jianjiashenzhen", "jianjia");
        levenshtein("jianjiashenzhen", "suoshu");

        double rate1 = 0.5;
        double rate2 = 0.5;

        log.info(levenshtein("张三应督促深圳蒹葭劳务公司马上整改", "蒹葭")*rate1 + rate2*getSimilarity("张三应督促深圳蒹葭劳务公司马上整改", "蒹葭"));
        log.info(levenshtein("张三应督促深圳蒹葭劳务公司马上整改", "硕鼠")*rate1 + rate2*getSimilarity("张三应督促深圳蒹葭劳务公司马上整改", "硕鼠"));
    }
}

